# -*- coding: utf-8 -*-
import os
import sys
import time
from datetime import datetime

sys.path.append((os.path.abspath(os.path.join(os.path.dirname(__file__), './'))))
import pytest
import pytest_html
from py.xml import html
from pytz import timezone
from projectConfig import feedback_appid, log_path
from sdktest_module.utils.constant import *
from sdktest_module.utils.device_manager import DeviceManger
from user import User
from utils.case_utils import case_params, uploadCaseInfos
from utils.config import Config

'''
pytest配置文件,跑用例会自动跑到这里
'''

startTime = 0  # 用例开始时间
caseResult = ''  # 用例成功失败


@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
    # 获取钩子方法的调用结果
    result = yield

    # 从钩子方法的调用结果中获取测试报告
    report = result.get_result()

    global caseResult
    caseResult = report.outcome if report.outcome == 'failed' else 'pass'
    extra = getattr(report, 'extra', [])
    if report.when == 'call':  # pytest_html_results_table_row仅仅在report.when == 'call'时被调用
        report.startTime = case_params['createTime']
        report.description = str(item.function.case_cn_name)
    elif report.when == 'teardown':
        # report.startTime = case_params['createTime']
        # report.description = str(item.function.case_cn_name)
        report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape")  # 设置编码显示中文
        # report_datacube(caseResult,  not is_platform_task)

        xfail = hasattr(report, 'wasxfail')
        if (report.skipped and xfail) or (report.failed and not xfail):
            # only add additional html on failure
            extra.append(pytest_html.extras.html('<div>Additional HTML</div>'))
        report.extra = extra


def pytest_addoption(parser):
    parser.addoption("-U", "--appid", action="store", default="2003426465", help="appid")
    parser.addoption("--uid1", action="store", default="217118", help="userA uid")
    parser.addoption("--uid2", action="store", default="217119", help="userB uid")
    parser.addoption("--uid3", action="store", default="217120", help="userC uid")
    parser.addoption("--uid4", action="store", default="217121", help="userD uid")
    parser.addoption("--uid5", action="store", default="217122", help="userE uid")
    parser.addoption("--uid6", action="store", default="217123", help="userF uid")
    parser.addoption("--uid7", action="store", default="217124", help="userG uid")
    parser.addoption("--uid8", action="store", default="217125", help="userH uid")
    parser.addoption("--uid9", action="store", default="217126", help="userI uid")
    parser.addoption("--uid10", action="store", default="217127", help="userJ uid")
    parser.addoption("--channel1", action="store", default="65935", help="channel1 id")
    parser.addoption("--channel2", action="store", default="73706", help="channel2 id")
    parser.addoption("--channel3", action="store", default="73707", help="channel3 id")
    parser.addoption("--devices", action="store", default="", help="deviceId，格式 did1,did2,did3")
    parser.addoption("--product_url", action="store", default="", help="product_url")
    parser.addoption("--config_file", action="store", default="", help="config file")
    parser.addoption("--taskId", action="store", default="0", help="taskId")


appid = None
uid1 = None
uid2 = None
uid3 = None
uid4 = None
uid5 = None
uid6 = None
uid7 = None
uid8 = None
uid9 = None
uid10 = None
channel1 = None
channel2 = None
channel3 = None
devicesFromCloud = None
product_url = ""
config_file = ""
taskId = ""
is_platform_task = False


def pytest_configure(config):
    global appid
    global uid1
    global uid2
    global uid3
    global uid4
    global uid5
    global uid6
    global uid7
    global uid8
    global uid9
    global uid10
    global channel1
    global channel2
    global channel3
    global devicesFromCloud
    global product_url
    global config_file
    global taskId
    global is_platform_task  # 是否时平台任务

    appid = config.getoption("--appid")
    uid1 = config.getoption("--uid1")
    uid2 = config.getoption("--uid2")
    uid3 = config.getoption("--uid3")
    uid4 = config.getoption("--uid4")
    uid5 = config.getoption("--uid5")
    uid6 = config.getoption("--uid6")
    uid7 = config.getoption("--uid7")
    uid8 = config.getoption("--uid8")
    uid9 = config.getoption("--uid9")
    uid10 = config.getoption("--uid10")
    channel1 = config.getoption("--channel1")
    channel2 = config.getoption("--channel2")
    channel3 = config.getoption("--channel3")
    devicesFromCloud = config.getoption("--devices")
    product_url = config.getoption("--product_url")
    config_file = config.getoption("--config_file")
    taskId = config.getoption("--taskId")
    is_platform_task = int(taskId) > 0


@pytest.hookimpl(optionalhook=True)
def pytest_html_results_table_header(cells):
    cells.insert(1, html.th('Start Time'))
    cells.insert(2, html.th('Description'))


@pytest.hookimpl(optionalhook=True)
def pytest_html_results_table_row(report, cells):
    # 此函数仅仅在report.when == 'call'时被调用
    if report.when == 'call':
        cells.insert(1, html.td(report.startTime))
        cells.insert(2, html.td(report.description))
    if report.when == 'teardown':
        cells.insert(1, html.td('-'))
        cells.insert(2, html.td('-'))


# ======setup teardown===start====
user_list = []
config = None
max_user_num = 2


@pytest.fixture(scope='session')
def init():
    global config
    if config_file != '':
        config = Config(config_path=os.path.abspath(os.path.join(os.path.dirname(__file__), "config")),
                        config_file=config_file)
    else:
        config = Config(config_path=os.path.abspath(os.path.join(os.path.dirname(__file__), "config")))

    global device_mgr
    device_mgr = DeviceManger()
    device_mgr.init_devices(config.getPort(), devicesFromCloud)
    return config, device_mgr


@pytest.fixture(scope='session', autouse=True)
def setup_session(init, request):
    """
    session setup:
    初始化：
    1. 配置
    2。设备
    3。用户
    """

    def wrapper():
        teardown_session(request)

    print("\n----------------------------session setup:{}--------------\n".format(request))
    request.addfinalizer(wrapper)
    global config, user_list

    if len(user_list) >= max_user_num:  # pytest 重试时，可能执行session setup
        return

    config, device_mgr = init

    device_list = device_mgr.get_devices(max_user_num)
    assert len(device_list) >= max_user_num, "在线设备数:{} <  用例需要的设备数:{}".format(len(device_list), max_user_num)
    uid_list = config.get_user_uid_list()
    account_list = config.get_user_account_list()
    password_list = config.get_user_password_list()
    assert len(uid_list) >= max_user_num, "配置账号个数:{} <  用例需要的账号数:{}".format(len(uid_list), max_user_num)
    for i in range(max_user_num):
        user = User(device_list[i], uid_list[i], get_role_by_index(i),
                    account_list[i],
                    password_list[i])
        user.driver.initialize()
        user_list.append(user)
        if product_url == '':
            print('product_url为空，不安装apk')
        else:
            user.driver.install_app(product_url)


def get_role_by_index(i):
    if i == 0:
        return 'userA'
    elif i == 1:
        return 'userB'
    elif i == 2:
        return 'userC'
    elif i == 3:
        return 'userD'
    elif i == 4:
        return 'userE'
    elif i == 5:
        return 'userF'
    elif i == 6:
        return 'userG'
    elif i == 7:
        return 'userH'
    elif i == 8:
        return 'userI'
    elif i == 9:
        return 'userJ'


def teardown_session(request):
    """
    session 结束：关闭连接
    """
    global user_list

    for user in user_list:
        if user.driver:
            print("{} finish".format(user.driver.device))
            user.driver.finish()
            print("{} finish done".format(user.driver.device))
    user_list = []
    print("\n-----------session teardown:{}-------------\n".format(request))


@pytest.fixture(scope='module', autouse=True)
def setup_module(init, request):
    """
    module setup:
    重启APP
    """
    print("\n-----------module setup{}-------------\n".format(request))
    for user in user_list:
        user.driver.disconnect()
        user.driver.stop_app()

    time.sleep(3)  # 等待关闭， 不在deviver中sleep, 可以减少sleep时长

    for user in user_list:
        user.driver.start_app()

    time.sleep(5)  # 等待app拉起， 不在deviver中sleep, 可以减少sleep时长

    for user in user_list:
        user.driver.connect()


@pytest.fixture(scope='class', autouse=True)
def setup_class(request):
    """
    class setup
    """
    request.cls.userA = user_list[0] if len(user_list) > 0 else None
    request.cls.userB = user_list[1] if len(user_list) > 1 else None
    request.cls.userC = user_list[2] if len(user_list) > 2 else None
    request.cls.userD = user_list[3] if len(user_list) > 3 else None
    request.cls.userE = user_list[4] if len(user_list) > 4 else None
    request.cls.userF = user_list[5] if len(user_list) > 5 else None
    request.cls.userG = user_list[6] if len(user_list) > 6 else None
    request.cls.userH = user_list[7] if len(user_list) > 7 else None
    request.cls.userI = user_list[8] if len(user_list) > 8 else None
    request.cls.userJ = user_list[9] if len(user_list) > 9 else None
    request.cls.config = config


@pytest.fixture(scope='function', autouse=True)
def setup_case(request):
    """
    用例开始
    初始化：用例信息
    """

    print("--------------------case setup:{}--------------------".format(request.function.case_func_name))

    def teardown_case_wrapper():
        teardown_case(request)

    request.addfinalizer(teardown_case_wrapper)

    def _formatCaseId(caseId):
        # 传入的id可能是String类型或者带有'-'字符，需要做一下特殊处理
        if type(caseId) == str and '-' in caseId:
            index = caseId.index('-')
            id = int(caseId[:index])
        else:
            id = int(caseId)
        return id

    for user in user_list:
        if user.driver.tcp_error:
            print("\n=====检测到%s网络异常，重连中……=====\n" % user.driver.role)
            user.driver.tcp_error = False
            user.driver.stop_app()
            time.sleep(3)
            user.driver.start_app()
            user.driver.connect()
            print("\n=====%s重连成功=====\n" % user.driver.role)
            time.sleep(5)

    def _sendExtInfo():
        # crash extInfo 格式：taskId_caseId_caseName_deviceInfo
        for user in user_list:
            if user.platform == ANDROID:
                deviceInfo = user.driver.device.brand + '-' + user.driver.device.serial
            elif user.platform == IOS:
                deviceInfo = 'Apple' + '-' + user.driver.device_id
            user.sendExtInfo(taskId + '_' + str(request.function.case_id) + '_' + request.function.case_func_name +
                             '_' + deviceInfo)

    if is_platform_task:
        uploadCaseInfos(request, user_list, taskId)

    assert hasattr(request.function, 'case_cn_name'), '请使用@case(id,name)设置用例id和中文名称'
    assert request.function.case_cn_name and len(request.function.case_cn_name.strip()), '@case(id,name)中文名称不能为空'

    case_params['appid'] = appid
    case_params['createBy'] = request.function.case_creator
    cst_tz = timezone('Asia/Shanghai')
    now = datetime.now(cst_tz)
    case_params['createTime'] = now.strftime('%Y-%m-%d %H:%M:%S')
    case_params['testcaseName'] = request.function.case_func_name
    case_params['testcaseNameCn'] = request.function.case_cn_name
    case_params['id'] = _formatCaseId(request.function.case_id)
    _clearBuff()
    # if is_platform_task:
    #     _sendExtInfo()


def teardown_case(request):
    """
    用例结束
    上报结果和日志等信息
    """
    cst_tz = timezone('Asia/Shanghai')
    now = datetime.now().replace(tzinfo=cst_tz)
    endTime = now.strftime('%Y-%m-%d %H:%M:%S')
    case_params['endTime'] = endTime
    # 清空缓存
    _clearBuff()
    # if caseResult != 'pass':
    #    feedback()
    time.sleep(2)
    print("\n--------------------Case End:{}--------------------\n".format(request.function.case_func_name))


def _clearBuff():
    for user in user_list:
        user.clearBuff()


def feedback():
    for user in user_list:
        user.feedback(feedback_appid, '2.9.15', user.uid,
                      'caseid:' + str(case_params['id']) + '-' + case_params['testcaseNameCn'], log_path)


@pytest.hookimpl(hookwrapper=True)
def pytest_collection(session):
    """
    获取session中所有class和用例中，最大的用户数/设备数
    """
    collect_timeout = 5
    collect_begin_time = time.time()
    yield

    case_user_num = [item.function.user_num for item in session.items if hasattr(item.function, 'user_num')]

    global max_user_num

    func_max_num = max(case_user_num) if case_user_num else max_user_num

    max_user_num = func_max_num

    collect_end_time = time.time()
    c_time = collect_end_time - collect_begin_time
    if c_time > collect_timeout:
        raise Exception('Collection timeout.')


def pytest_collection_modifyitems(items):
    """
    根据用例支持平台进行标记
    """
    for item in items:
        platform_list = item.function.case_platform
        if ANDROID in platform_list:
            item.add_marker(pytest.mark.android)
        if IOS in platform_list:
            item.add_marker(pytest.mark.ios)
        if WINDOWS in platform_list:
            item.add_marker(pytest.mark.win)
        if MAC in platform_list:
            item.add_marker(pytest.mark.mac)

# ======setup teardown===end====
